/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://code.google.com/p/dylab/

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_EntityDefinitionManager_h__
#define __dylab_EntityDefinitionManager_h__

//===========================================================================================
#include <dylab/utils/Singleton.h>
#include <dylab/utils/containers/HashObjContainer.h>
#include <dylab/utils/property/PropertyTree.h>

//===========================================================================================
namespace dylab {
    
    /** Entity definition manager.
	@remarks
		This class manage definitions represented by property trees.
		Each definition belongs exactly to one entity.
    */    
    class DYLAB_EXPORT EntityDefinitionManager 
        : public Singleton<EntityDefinitionManager>
    {
	public:
		// forward declaration
		class DYLAB_EXPORT Entity;

    public:
        /// Definition is identified by its name and belongs to an entity.
        class DYLAB_EXPORT Definition
        {
        private:
			Entity * mParentEntity;
            String mName;
			PropertyTree mPropertyTree;
        public:
            Definition(Entity * _parentEntity, const String & _name, const PropertyTree & _pt)
                : mParentEntity(_parentEntity), mName(_name), mPropertyTree(_pt)
                    { }
            virtual ~Definition()
                    { }
			Entity * getParentEntity() 
					{ return mParentEntity; }
			const Entity * getParentEntity() const
					{ return mParentEntity; }
            String getName() const
                    { return mName; }
			PropertyTree & getPropertyTree()
					{ return mPropertyTree; }
			const PropertyTree & getPropertyTree() const
					{ return mPropertyTree; }
        };
        //-----------------------------------------------------------------------------

    public:
        // definition entity map
        class DYLAB_EXPORT DefinitionMap 
            : public HashObjContainer<String, Definition> 
        {
        public:
            DefinitionMap()
                : HashObjContainer<String, Definition>(true)
                    { }
        };
        //-----------------------------------------------------------------------------

	public:
        /// Definition entity is identified by its name and belongs to the manager.
        class DYLAB_EXPORT Entity
        {
        private:
			EntityDefinitionManager * mParentEDM;
            String mName;
			DefinitionMap mDefinitionMap;
        public:
            Entity(EntityDefinitionManager * _parentEDM, const String & _name)
                : mParentEDM(_parentEDM), mName(_name)
                    { DYLAB_ASSERT(mParentEDM != NULL); }
            virtual ~Entity()
                    { }
            String getName() const
                    { return mName; }
			DefinitionMap & getDefinitionMap()
					{ return mDefinitionMap; }
			const DefinitionMap & getDefinitionMap() const
					{ return mDefinitionMap; }
			Definition * getDefinition(const String & _defName);
			const Definition * getDefinition(const String & _defName) const;
        };
        //-----------------------------------------------------------------------------

    public:
        // definition entity map
        class DYLAB_EXPORT EntityMap 
            : public HashObjContainer<String, Entity> 
        {
        public:
            EntityMap()
                : HashObjContainer<String, Entity>(true)
                    { }
        };
        //-----------------------------------------------------------------------------

	public:
		template <class _EntityClass> 
		class EntityBase
		{
		private:
			/// Name of the associated entity
			static String s_mEntityName;

	    public:
			/// Returns the name of the associated entity.
			static String getEntityName()
					{ return s_mEntityName; }

			/// Adds new entity with the given name to the entity definition manager and store the name.
			static void registerEntity(const String & _entityName)
					{ s_mEntityName = _entityName; EntityDefinitionManager::getSingleton().addEntity(s_mEntityName); }

			/// Retrieve the definition with the given name.
			static EntityDefinitionManager::Definition * getEntityDefinition(const String & _defName)
					{ return EntityDefinitionManager::getSingleton().getEntity(s_mEntityName)->getDefinition(_defName); }

		};
		//-----------------------------------------------------------------------------

    private:
        /// Base path for definition files.
        String mBasePath;
        /// Resource entity container.
        EntityMap mEntityMap;

    public:

        EntityDefinitionManager();
        virtual ~EntityDefinitionManager();

        void setBasePath(const String & _basePath)
                { mBasePath = _basePath; }
        String getBasePath() const
                { return mBasePath; }

        inline EntityMap & getEntityMap()
				{ return mEntityMap; }
        inline const EntityMap & getEntityMap() const
				{ return mEntityMap; }


        /** Adds and returns new definition entity with the specified name.
		@param[in] _entityName
			The name of the entity.
        */
        Entity * addEntity(const String & _entityName);

		/** Returns previously added entity object with the given name.
		@param[in] _entityName
			The name of the entity.		
		*/
		Entity * getEntity(const String & _entityName);

		/** Returns previously added entity object with the given name, const version.
		@param[in] _entityName
			The name of the entity.		
		*/
		const Entity * getEntity(const String & _entityName) const;

        /** Loads the definitions from the given file.
		@remarks
			The file name is prefixed with the base path string.
		@param[in] _fileName
			The name of file which contains definitions in the form of property trees.		
        */
		void loadDefinitionFile(const String & _fileName);

        /** Recursively expands all "@base" attributes of the given property tree.
		@param[in] _pt
			The property tree that should be expanded.
		@param[in] _entityName
			The name of an entity that contains the definition in the form of the given tree.
        */
		void expandPropertyTree(const String & _entityName, PropertyTree & _pt);

        /** Loads the whole definition manager from the given configuration file.
		@param[in] _fileName
			The name of the file containing a settings for this manager.
        */
		void loadFromConfigFile(const String & _fileName);
	};

	//===========================================================================================
	/// Generic initialization of the static member.
	template <class _EntityClass> String EntityDefinitionManager::EntityBase<_EntityClass>::s_mEntityName;
}
//===========================================================================================
#endif // __dylab_EntityDefinitionManager_h__
